#include "../stdlib.hpp"
#include <purewell/code/escape.hpp>

namespace purewell
{

PWEscape& gPWEscape(PWEscape::getInstance());

inline
static
bool
_addEscape(char& o, char c, char e)
{
	switch (c)
	{
	case ' ': o = ' '; return true;
	case '\r': o = 'r'; return true;
	case '\n': o = 'n'; return true;
	case 0x00: o = '0'; return true;
	case '\t': o = 't'; return true;
	case '\f': o = 'f'; return true;
	case '\v': o = 'v'; return true;
	case '\'': o = '\''; return true;
	case '\"': o = '\"'; return true;
	}

	if ( c == e )
	{
		o = c;
		return true;
	}

	return false;
}

inline
static
char
_delEscape(char c, char e)
{
	switch (c)
	{
	case ' ': return ' ';
	case 'r': return '\r';
	case 'n': return '\n';
	case '0': return 0x00;
	case 't': return '\t';
	case 'f': return '\f';
	case 'v': return '\v';
	case '\'': return '\'';
	case '\"': return '\"';
	}

	return c;
}

PWEscape::PWEscape()
{
}

PWEscape&
PWEscape::getInstance(void)
{
	static PWEscape inst;
	return inst;
}

std::ostream&
PWEscape::encode(std::ostream& os, std::istream& is, char e) const
{
	char c(0x00);
	char o;

	while ( !is.eof() )
	{
		is.get(c);
		if ( !is.good() ) break;

		if ( _addEscape(o, c, e) )
		{
			os << e << o;
		}
		else
		{
			os << c;
		}
	}

	return os;
}

size_t
PWEscape::encode(char* obuf, const char* ibuf, size_t iblen, char e) const
{
	const char* ib(ibuf);
	const char* ie(ibuf+iblen);
	size_t ret(0);

	char c, o;

	while ( ib != ie )
	{
		c = *ib;
		if ( _addEscape(o, c, e) )
		{
			*(obuf+ret) = e;
			++ret;
			*(obuf+ret) = o;
		}
		else
		{
			*(obuf+ret) = c;
		}

		++ret;
		++ib;
	}

	return ret;
}

std::ostream&
PWEscape::decode(std::ostream& os, std::istream& is, char e) const
{
	char c(0x00);
	bool raw(true);

	while ( !is.eof() )
	{
		is.get(c);
		if ( !is.good() ) break;

		if ( raw )
		{
			if ( c == e )
			{
				raw = false;
			}
			else
			{
				os << c;
			}
		}
		else
		{
			os << _delEscape(c, e);
			raw = true;
		}
	}

	return os;
}

size_t
PWEscape::decode(char* obuf, const char* ibuf, size_t iblen, char e) const
{
	char c;
	bool raw(true);

	const char* ib(ibuf);
	const char* ie(ibuf+iblen);
	size_t ret(0);

	while ( ib != ie )
	{
		c = *ib;

		if ( raw )
		{
			if ( c == e )
			{
				raw = false;
			}
			else
			{
				*(obuf+ret) = c;
				++ret;
			}
		}
		else
		{
			*(obuf+ret) = _delEscape(c, e);
			++ret;

			raw = true;
		}

		++ib;
	}

	return ret;
}

};//namespace purewell


